import java.util.HashMap;

public class CoocurenceMatrix
{
    int values[][];
    double normalizedValues[][];
    int levels;
    double sum = 0;
    double moment = 0;
    double kontrast = 0;
    double korelacja = 0;
    double entropia = 0;


    public CoocurenceMatrix(int l)
    {
        levels = l;
        values = new int[l][l];
    }

    public CoocurenceMatrix calculateParameters(){
        normalize();
        liczMoment();
        liczKontrast();
        liczEntropie();
        liczKorelacje();
        return this;
    }

    public void inc(int x, int y){
        values[x][y]++;
        sum++;
    }

    private void normalize(){
        normalizedValues = new double[levels][levels];

        for(int i = 0; i < levels; ++i){
            for(int j = 0; j < levels; ++j){
                normalizedValues[i][j] = (double)values[i][j]/sum;
            }
        }
    }

    private void liczMoment(){
        for(int i = 0; i < levels; ++i){
            for(int j = 0; j < levels; ++j){
                moment += normalizedValues[i][j]*normalizedValues[i][j];
            }
        }
    }

    private void liczKontrast(){
        for(int l = 1; l < levels; ++l){
            for(int i = 0; i < levels; ++i){
                for(int j = 0; j < levels; ++j){
                    if(Math.abs(i - j) == l){
                        //System.out.println("l: " + l + " ij:" + i + " " + j + " " + normalizedValues[i][j]);
                        kontrast +=  l*l*normalizedValues[i][j];
                    }
                }
            }
        }

    }

    private void liczKorelacje(){
        float[] means_i = new float[levels];
        float[] means_j = new float[levels];
        float sum = 0f;
        float sqrt;

        for(int i = 0; i < levels; ++i){
            means_i[i] = mean(i,false);
            means_j[i] = mean(i,true);
        }

        for(int i = 0; i < levels; ++i){
            for(int j = 0; j < levels; ++j){
               // System.out.println("" + standardDeviation(i,means_i[i],false) + "   " +  standardDeviation(j,means_j[j],true));
                sqrt = (float)Math.sqrt(standardDeviation(i,means_i[i],false)*standardDeviation(j,means_j[j],true));
                korelacja += normalizedValues[i][j]*(((i - means_i[i])*(j - means_j[j]))/sqrt==0?0:sqrt);
            }
        }
    }

    private float mean(int ij, boolean vertical){
        float mean = 0f;
        for(int i = 0; i < levels; ++i){
            mean += vertical?normalizedValues[i][ij]:normalizedValues[ij][i];
        }
        return mean/levels;
    }

    private float standardDeviation(int ij, float mean, boolean vertical){
        float deviation = 0f;
        double pow;
        for(int i = 0; i < levels; ++i){
            pow = vertical?normalizedValues[i][ij]:normalizedValues[ij][i];
            deviation += (pow - mean)*(pow - mean);
        }
        return deviation/levels;
    }

    private void liczEntropie(){
        for(int i = 0; i < levels; ++i){
            for(int j = 0; j < levels; ++j){
                //TODO co z tym log()
                entropia += normalizedValues[i][j]*(normalizedValues[i][j]!=0?Math.log(normalizedValues[i][j]):0);
            }
        }
        entropia *= -1;
    }

    public void add(CoocurenceMatrix c){
        for(int i = 0; i < levels; ++i){
            for(int j = 0; j < levels; ++j){
                values[i][j] += c.values[i][j];
            }
        }
        sum += c.sum;
    }

    public double calculateDistanceClassifier(CoocurenceMatrix c, HashMap<String,Double> maxValues){
        double mom, kon, ent, kor;
        mom = (moment - c.moment)/maxValues.get("moment");
        kon = (kontrast - c.kontrast)/maxValues.get("kontrast");
        ent = (entropia - c.entropia)/maxValues.get("entropia");
        kor = (korelacja - c.korelacja)/maxValues.get("korelacja");
        return Math.sqrt( mom*mom + kon*kon + ent*ent + kor*kor );
    }

    public void printParameters(){
        System.out.println("Moment ["+moment+"]\tKontrast [" + kontrast +"]\tEntropia [" +entropia+"]\tKorelacja ["+korelacja+"]");
    }
    
    public enum Direction { HORIZONTAL, VERTICAL, LR_SLANTED, RL_SLANTED };
}
